/**
 * 包含一些会用到的工具方法
 */
#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include"Log.hpp"
#include<atomic>
#include<sys/time.h>
#include<boost/algorithm/string.hpp>
namespace OnlineOJ
{
    namespace ns_Util
    {
        // 定义日志宏常量简化传参
    #define LOG(level, format, ...)                                                  \
        do                                                                           \
        {                                                                            \
            LogMessage(__FILE__, __LINE__, Save_File, level, format, ##__VA_ARGS__); \
        } while (0)
    //"__FILE__" 能自动获取当前文件名字 ; "__LINE__" 能自动获取当前代码所在的行数
    //"__VA_ARGS__" 处理可变参数的宏，但是当可变参数为空时，会出问题，一般使用"##__VA_ARGS__"

    // 定义宏控制是否保存日志信息到日志文件中
    #define SAVEFILE()        \
        do                    \
        {                     \
            Save_File = true; \
        } while (0)

    #define SAVESCREEN()       \
        do                     \
        {                      \
            Save_File = false; \
        } while (0)

        const std::string defaultpath="./temp/";//默认路径
        //路径拼接工具类
        class PathUtil
        {
        public:
            //构建传入的后缀，拼接路径
            static std::string AddSuffix(const std::string& filename,const std::string& suf)
            {
                std::string path_name=defaultpath;
                path_name+=filename;
                path_name+=suf;
                return path_name;
            } 

            //编译时需要的临时文件
            //构建完整源文件名，路径+文件名+后缀
            static std::string Source(const std::string& filename)
            {
                return AddSuffix(filename,".cpp");
            }
            //构建完整可执行程序，路径+后缀
            static std::string Exe(const std::string& filename)
            {
                return AddSuffix(filename,".exe");
            }
            //构建程序编译时对应的标准错误，路径+后缀
            static std::string CompileError(const std::string& filename)
            {
                return AddSuffix(filename,".compile");
            }

            //运行时需要的临时文件
            //构建标准输入文件
            static std::string StdIn(const std::string& filename)
            {
                return AddSuffix(filename,".stdin");
            }
            //构建标准输出文件
            static std::string StdOut(const std::string& filename)
            {
                return AddSuffix(filename,".stdout");
            }
            //构建程序运行时对应的标准错误，路径+后缀
            static std::string StdError(const std::string& filename)
            {
                return AddSuffix(filename,".stderr");
            }
        };

        class TimeUtil
        {
        public:
            //获取毫秒级时间戳
            static std::string GetTimeMs()
            {
                struct timeval t;
                gettimeofday(&t,nullptr);
                return std::to_string(t.tv_sec*1000+t.tv_usec/1000);
            }
        };

        //文件相关操作
        class FileUtil
        {
        public:
            //判断文件是否存在
            static bool IsFileExists(const std::string& pathname)
            {
                struct stat st;
                //int stat(const char *__restrict__ __file, stat *__restrict__ __buf)
                //获取文件属性到__buf中
                //成功返回0，失败返回-1
                int ret=stat(pathname.c_str(),&st);
                if(ret==0)
                {
                    return true;
                }
                return false;
            }

            //生成唯一的随机文件名(没有路径和后缀)
            static std::string UniqueFileName()
            {
                //通过毫秒级时间戳和一个原子性递增的静态变量，保证唯一性
                static std::atomic<int> id(0);//一个原子性递增的静态值
                id++;

                std::string ms=TimeUtil::GetTimeMs();
                std::string ID=std::to_string(id);
                std::string unique_name=ms+"_"+ID;

                return unique_name;
            }

            //把code中的内容写入到filename文件中
            static bool WriteFile(const std::string& filename,const std::string& content)
            {
                std::ofstream out(filename);
                if(!out.is_open())
                {
                    return false;
                }
                out.write(content.c_str(),content.size());
                out.close();
                return true;
            }

            //从文件中读取数据
            static bool ReadFile(const std::string& filename,std::string& content,const bool flag=false)
            {
                content.clear();//清空数据

                std::ifstream in(filename);
                if(!in.is_open())
                {
                    return false;
                }
                std::string line;
                //getline默认不保存换行符'\n'
                while(std::getline(in,line))
                {
                    content+=line;
                    if(flag)
                    {
                        content+='\n';
                    }
                }
                in.close();
                return true;
            }
        };

        //字符串操作
        class StringUtil
        {
        public:
            //切分字符串
            //把src字符串以sep为分隔符进行切分后的子字符串存进res中
            static int SplitString(const std::string& src,std::vector<std::string>& res,const std::string& sep)
            {
                // size_t pos=0,index=0;
                // while(index<src.size())
                // {
                //     pos=src.find(sep,index);
                //     if(pos==std::string::npos)//找到最后一个
                //     {
                //         res.emplace_back(src.substr(index));
                //         break;
                //     }
                //     if(pos==index)//出现连续的分隔符
                //     {
                //         index+=sep.size();
                //         continue;
                //     }
                //     res.emplace_back(src.substr(index,pos-index));
                //     index=pos+sep.size();
                // }

                //借助boost库split函数实现字符串分割
                //boost::algorithm::token_compress_on：出现多个连续分隔符时不保留空串
                //boost::algorithm::token_compress_off：出现多个连续分隔符时保留空串
                boost::split(res,src,boost::is_any_of(sep),boost::algorithm::token_compress_on);
                return res.size();
            } 
        };
    }
}